package epoch.game.core.distr.config;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import epoch.game.core.distr.NodeType;
import epoch.game.core.exception.ConfigException;
import epoch.game.core.util.Prop;
import epoch.game.core.util.StrKit;

/**
 * 服务节点配置
 * @author will
 * @date 2015年11月28日 上午11:06:35
 */
public class NodeConfig {

	//服务节点配置 key：nodeName
	static HashMap<String, NodeConfig> configs = new HashMap<String, NodeConfig>();
	//Service服务所在节点配置 key:ServiceName (只有本服的结点)
	public static HashMap<String,List<Integer>> servToNode = new HashMap<String, List<Integer>>();
	// id
	private int id;
	// 服务器id
	private int serverId;
	// 服务节点名称
	private String name;
	// 节点地址
	private String host;
	// 端口
	private int port;
	// 子节点
	private String remoteNodeNames;
	// 远程子节点key
	private Set<String> remoteNodes = new HashSet<String>();
	// 节点类型
	private NodeType type;
	//本节点注册的服务
	public List<String> services;
	// 地图组
	private List<Integer> mapGroups = new ArrayList<Integer>();
	//本节点分线起始
	public int lineFrom;
	//本节点最大分线
	public int lineMax;

	//-----------------------------------------
	public static int serverId(int nodeId) {
		return nodeId/100;
	}
	public static int nId(int nodeId) {
		return nodeId%100;
	}
	public static int nodeId(int serverId, int nid) {
		return serverId*100 + nid;
	}
		
	//-----------------------------------------
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	
	public int getServerId() {
		return serverId;
	}
	
	public void setServerId(int serverId) {
		this.serverId = serverId;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getRemoteNodeNames() {
		return remoteNodeNames;
	}

	public void setRemoteNodeNames(String remoteNodeNames) {
		this.remoteNodeNames = remoteNodeNames;
	}

	public Set<String> getRemoteNodes() {
		return remoteNodes;
	}

	public void setRemoteNodes(Set<String> remoteNodes) {
		this.remoteNodes = remoteNodes;
	}

	public List<Integer> getMapGroups() {
		return mapGroups;
	}

	public void setMapGroups(List<Integer> mapGroups) {
		this.mapGroups = mapGroups;
	}

	/**
	 * 根据节点名称获取节点配置
	 * @param nodeName
	 * @return
	 */
	public static NodeConfig getConfig(String nodeName){
		return configs.get(nodeName);
	}
	
	public static Set<String> allNodesName() {
		return configs.keySet();
	}
	
	/**
	 * 根据节点id获取节点配置
	 * @param nodeId
	 * @return
	 */
	public static NodeConfig getConfigById(int nodeId){
		Iterator<Entry<String, NodeConfig>> iterator = configs.entrySet().iterator();
		while(iterator.hasNext()){
			Entry<String, NodeConfig> config = iterator.next();
			int conf = config.getValue().getId();
			if(conf==nodeId){
				return config.getValue();
			}
		}
		return null;
	}
		
//	public boolean isPassive() {
//		return passive;
//	}
//
//	public void setPassive(boolean passive) {
//		this.passive = passive;
//	}
	
	public NodeType getType() {
		return type;
	}

	public void setType(NodeType type) {
		this.type = type;
	}

	/**
	 * 加载配置
	 * @param serverName
	 * @param configPath
	 */
	public static void loadConfig(int serverId, String configPath) throws ConfigException{
		HashMap<String, NodeConfig> newConfigs = new HashMap<>();
		HashMap<String,List<Integer>> newServToNode =new HashMap<>();
		try {
			Prop prop = Prop.load(configPath);
			Properties property = prop.getProperties();
			property.forEach((key, val) -> {
				String keyStr = key.toString();
				String[] split = keyStr.split("\\.");
				//节点名称
				String nodeName = split[0];
				NodeConfig config = null;
				if (!newConfigs.containsKey(nodeName)) {
					config = new NodeConfig();
					config.setName(nodeName);
					newConfigs.put(nodeName, config);
					
					//节点id
					//nodeId-----------------------------
					Integer nodeId = prop.getInt(nodeName+".nodeId");
					if(nodeId==null){
						throw new ConfigException("节点配置错误："+nodeName+"没有配置nodeId!");
					}
					int sid = NodeConfig.serverId(nodeId);
					if (sid == 0) {
						throw new ConfigException("节点配置错误："+nodeName+"配置的nodeId不符合要求，不满足serverId*1000+nodeId!");
					}					
					config.setId(nodeId);
					config.setServerId(sid);
					
					//地址和端口 (eg: 127.0.0.1:8000)
					//nodeAddress-----------------------------
					String nodeAddress = prop.get(nodeName+".nodeAddress");
					if(StrKit.isBlank(nodeAddress)){
						throw new ConfigException("节点配置错误："+nodeName+"没有配置nodeAddress!");
					}
					String[] valsplit = nodeAddress.trim().split(":");
					config.setHost(valsplit[0]);
					config.setPort(Integer.parseInt(valsplit[1]));
					
					//类型GAME/GATE
					//type-----------------------------
					String type = prop.get(nodeName+".type");
					if(StrKit.isBlank(type)){
						throw new ConfigException("节点配置错误："+nodeName+"没有配置type!");
					}
					type = type.trim().toUpperCase();
					NodeType nodeType = NodeType.valueOf(type);
					if(nodeType==null){
						throw new ConfigException("节点配置错误：can not recognize the node("+config.name+") type :"+type);
					}
					config.setType(nodeType);
					//设置默认节点
					if(DConfig.NODE_WORLD==0 && nodeType==NodeType.GAME){
						DConfig.NODE_WORLD=config.id;
					}else if(nodeType==NodeType.DB && DConfig.NODE_DB==0){
						DConfig.NODE_DB=config.id;
					}
					if(DConfig.NODE_WORLD!=0 && nodeType==NodeType.GAME){
						DConfig.IS_SINGLE_WORLD=false;
					}
					//远程节点名称
					//remoteChild-----------------------------
					String remoteChild = prop.get(nodeName+".remoteChild");
					if(!StrKit.isBlank(remoteChild)){
						remoteChild = remoteChild.trim();
						config.setRemoteNodeNames(remoteChild);
						valsplit = remoteChild.split(",|，");
						for (String v : valsplit) {
							config.getRemoteNodes().add(v);
						}
					}
					
					//服务配置
					//service-----------------------------
					//if (sid == serverId) { // 本服结点
					if (true) {
						String service = prop.get(nodeName+".service");
						if(!StrKit.isBlank(service)){
							service=service.trim();
							String[] servs = service.split(",|，");
							for(String serv : servs){
								String servKey = serv.toUpperCase();
								if(!newServToNode.containsKey(servKey)){
									newServToNode.put(servKey, new ArrayList<Integer>());
								}
								newServToNode.get(servKey).add(config.getId());
							}
						}
					}
					//地图组分布
					//mapGroups-----------------------------
					String mapGroupStr = prop.get(nodeName+".map.groups");
					if(!StrKit.isBlank(mapGroupStr)){
						mapGroupStr = mapGroupStr.trim();
						valsplit = mapGroupStr.split(",|，");
						for (String v : valsplit) {
							config.getMapGroups().add(Integer.parseInt(v.trim()));
						}
					}
					
					//分线配置
					//line-----------------------------
					String line = prop.get(nodeName+".map.lines");
					if(!StrKit.isBlank(line)){
						line = line.trim();
						valsplit = line.split("_");
						if(valsplit.length!=2){
							throw new ConfigException("节点配置错误："+nodeName+" 分线配置格式错误（eg:1_10） :"+line);
						}
						config.lineFrom=Integer.parseInt(valsplit[0].trim());
						config.lineMax=Integer.parseInt(valsplit[1].trim());
					}
				} 
			});
			configs = newConfigs;
			servToNode = newServToNode;
			//初始化地图分配数据
			initMapDistrConfig(serverId);
		} catch (Exception ex) {
			throw new ConfigException("node config err! ex="+ex,ex);
		}
	}

	/**
	 * 初始化地图分配
	 */
	private static void initMapDistrConfig(int serverId){
		Map<Integer, List<MapDistrConfig>> mapLocation = new HashMap<>();
		for(NodeConfig nodeConf : NodeConfig.configs.values()){
			if(nodeConf.getServerId() == serverId && 
			   (nodeConf.getType()==NodeType.GAME || nodeConf.getType()==NodeType.CROSS)){
				for(Integer mapGroup : nodeConf.getMapGroups()){
					MapDistrConfig mapConf = new MapDistrConfig();
					mapConf.mapGroup = mapGroup;
					mapConf.nodeId = nodeConf.getId();
					mapConf.lineFrom = nodeConf.lineFrom;
					mapConf.lineMax = nodeConf.lineMax;
					List<MapDistrConfig> mapConfList = mapLocation.get(mapGroup);
					if(mapConfList==null){
						mapConfList = new ArrayList<>();
						mapLocation.put(mapGroup, mapConfList);
					}
					mapConfList.add(mapConf);
				}
			}
		}
		MapDistrConfig.mapLocation = mapLocation;
	}
}
